package com.sumer.psychology.statistics.infrastructure;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.sumer.psychology.common.domain.model.User;
import com.sumer.psychology.common.domain.model.UserService;
import com.sumer.psychology.common.persistence.mongodb.AbstractMongoRepository;
import org.apache.commons.collections.CollectionUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

/**
 * 记录访问信息
 * <p/>
 * Created by twer on 15/3/13.
 */
public class MongoVisitStatisticsRepository extends AbstractMongoRepository {
    private static final String DB_NAME = "sumer_visitor_statistics";

    private int visitorLimit;
    private UserService userService;

    public MongoVisitStatisticsRepository(int visitorLimit, UserService userService) {
        this.visitorLimit = visitorLimit;
        this.userService = userService;
    }

    /**
     * 增加一个记录
     *
     * @param userId    user
     * @param visitorId visitor
     */
    public void add(String userId, String visitorId) {
        if (visitorExistForUser(userId, visitorId)) {
            updateVisitTime(userId, visitorId);
            return;
        }

        if (needPop(userId)) {
            pop(userId);
        }
        addVisitor(userId, visitorId);
    }

    /**
     * json 封装
     *
     * @param userId user id
     * @return json
     */
    public String visitors(String userId) {
        BasicDBList visitors = findVisitors(userId);
        return serialize(visitors);
    }

    /**
     * 查询访问者
     *
     * @param userId user id
     * @return visitors
     */
    public BasicDBList findVisitors(String userId) {
        DBObject user = findOne(userId);
        if (user == null) {
            return new BasicDBList();
        }
        BasicDBList visitors = (BasicDBList) user.get("visitors");
        Collections.sort(visitors, new VisitTimeComparator());
        return visitors;
    }

    /*
     * 更新访问时间
     */
    private void updateVisitTime(String userId, String visitorId) {
        db().getCollection(dbName()).update(
                new BasicDBObject("_id", userId).append("visitors.id", visitorId),
                new BasicDBObject(
                        new BasicDBObject("$set",
                                new BasicDBObject("visitors.$.visit_time", new Date()))));
    }

    /*
     * 访问者是否已经记录过
     */
    private boolean visitorExistForUser(String userId, String visitorId) {
        return db().getCollection(dbName()).find(
                new BasicDBObject("_id", userId)
                        .append("visitors.id", visitorId))
                .size() > 0;
    }

    /*
     * 添加
     */
    private void addVisitor(String userId, String visitorId) {
        User user = userService().userFrom(visitorId);

        DBObject visitor = new BasicDBObject()
                .append("id", visitorId)
                .append("name", user.name())
                .append("visit_time", new Date());

        db().getCollection(dbName()).update(
                new BasicDBObject("_id", userId),
                new BasicDBObject("$push", new BasicDBObject("visitors", visitor)),
                true,
                false);
    }

    /*
     * 是否需要弹出最早的
     */
    private boolean needPop(String userId) {
        DBObject result = findOne(userId);
        if (result != null) {
            BasicDBList visitors = (BasicDBList) result.get("visitors");
            return CollectionUtils.isNotEmpty(visitors) && visitors.size() >= visitorLimit();
        }
        return false;
    }

    /*
     * 弹出最早的
     */
    private void pop(String userId) {
        db().getCollection(dbName()).update(
                new BasicDBObject("_id", userId),
                new BasicDBObject("$pop", new BasicDBObject("visitors", -1)));
    }

    @Override
    protected String dbName() {
        return DB_NAME;
    }

    private int visitorLimit() {
        return visitorLimit;
    }

    private UserService userService() {
        return userService;
    }
}

class VisitTimeComparator implements Comparator<Object> {
    @Override
    public int compare(Object o1, Object o2) {
        DBObject d1 = (DBObject) o1;
        DBObject d2 = (DBObject) o2;

        Date o1Time = (Date) d1.get("visit_time");
        Date o2Time = (Date) d2.get("visit_time");
        return o2Time.compareTo(o1Time);
    }
}